#include <thread>
#include <posix.h>
#include <framework.h>
#include <utils.h>
#include <log.h>

LOCAL const BYTE Major_Version_Number = 2;
LOCAL const BYTE Minor_Version_Number = 1;
LOCAL const WORD Revision_Number = 7;
LOCAL const char Compile_date[] = __DATE__;
LOCAL const char Compile_time[] = __TIME__;

std::string FrameWorkMgr::getVersion()
{

   return utils::format("v%d.%d.%d", Major_Version_Number, Minor_Version_Number, Revision_Number);
}

std::string FrameWorkMgr::getBuildTime()
{
    return utils::format("%s %s", Compile_date, Compile_time);
}
LOCAL std::ostringstream& welcome(std::ostringstream& oss)
{
    oss << std::endl
        << "    ____  __________  ____" << std::endl
        << "   / __ \\/ ____/ __ \\/ __ \\" << std::endl
        << "  / / / / /   / / / / /_/ /" << std::endl
        << " / /_/ / /___/ /_/ / ____/" << std::endl
        << "/_____/\\____/\\____/_/" << std::endl
        << std::endl
        << " Release version : " << GET_FRAMEWORK_MGR->getVersion() << std::endl
        << " Build date : " << GET_FRAMEWORK_MGR->getBuildTime() << std::endl
        << std::endl;

    return oss;
}

void FrameWorkMgr::welcome(int fd)
{
    std::ostringstream oss;
    LVOS_Write(::welcome(oss), fd);
}

void FrameWorkMgr::welcome(SessionPtr session)
{
    std::ostringstream oss;
    LVOS_Write(::welcome(oss), session);
}

STATUS FrameWorkMgr::waitExit()
{
    COND_CHECK_RET_ERROR(init() != OK);
    while (!m_stoped.load())
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(1000));
    }
    COND_CHECK_RET_ERROR(destory() != OK);
    return OK;
}

STATUS FrameWorkMgr::setExit()
{
    m_stoped.store(true);
    return OK;
}

STATUS FrameWorkMgr::objRegister(const char* name , objbase * object, int objId)
{
    std::lock_guard<std::mutex> lock(m_objectLock);
    COND_CHECK_RET_ERROR(queryObjectInternal(objId) != nullptr);

    auto pFWk = std::make_shared<FrameWork>(name, object, objId);
    COND_CHECK_RET_ERROR(pFWk == nullptr);

    m_objectList[objId] = pFWk;
    return OK;
}

objbase* FrameWorkMgr::queryObjectInternal(int objectId, bool isRef)
{
    auto it = m_objectList.find(objectId);

    COND_CHECK_LOG_RET_VAL(it == m_objectList.end(),
        nullptr, "objectId %#x not found", objectId);

    LOG_TRACE("objectName %s objectPtr %s objectId %#x refCount %d",
        it->second->objectName.c_str(), utils::to_hexstring(it->second->objectPtr).c_str(),
        objectId, it->second->refCount);

    if (isRef)
    {
        it->second->refCount++;
    }

    return it->second->objectPtr;
}

objbase *FrameWorkMgr::queryRefObject(int objectId)
{
    std::lock_guard<std::mutex> lock(m_objectLock);
    return queryObjectInternal(objectId, true);
}

STATUS FrameWorkMgr::releaseRefObject(int objectId)
{
    return objUnRegister(objectId);
}

FrameWorkMgrCollector::FrameWorkMgrCollector(const char* name , objbase * object, int objId)
{
   GET_FRAMEWORK_MGR->objRegister(name, object, objId);
}

STATUS FrameWorkMgr::objUnRegister(int objectId)
{
    std::lock_guard<std::mutex> lock(m_objectLock);
    auto it = m_objectList.find(objectId);

    COND_CHECK_RET_ERROR(it == m_objectList.end());

    if (_LIKELY(it->second->refCount > 0))
    {
        it->second->refCount--;
        return OK;
    }

    m_objectList.erase(it);

    return OK;
}

STATUS FrameWorkMgr::init()
{
    UINT64 entrytime = 0;
    UINT64 endtime = 0;

    m_stoped.store(false);
    entrytime = utils::GetCurrentMillisecs();

    FrameWorkMgr::welcome();

    Ilog::logInitial();    

    auto _init_modules = [=](const std::string& phaseName,
        std::function<STATUS(objbase*)> initFunc)
        {
            for (auto& iter : m_objectList)
            {
                auto moduleEntry = iter.second;
                std::ostringstream oss;
                oss << phaseName << " objectName " << moduleEntry->objectName
                    << " objectId " << std::setfill('0') << std::hex 
                    << moduleEntry->objectId << std::endl;
                
                LVOS_Write(oss.str(), STDOUT_FILENO);

                if (initFunc(moduleEntry->objectPtr) != OK)
                {
                    std::ostringstream oss;
                    oss << "FATAL --->>>>Failed to " << phaseName << " moudle:"
                        << std::setfill('0') << std::hex 
                        << moduleEntry->objectName << std::endl;
                    oss << "\tmodule ID: " << moduleEntry->objectId << std::endl;
                    LOG_FATAL(oss.str().c_str());
                    LVOS_Write(oss.str().c_str(), STDOUT_FILENO);
                    return RET_ERROR;
                }
            }
            return OK;
        };
    // per-initial all module
    if (_init_modules("preInit", [](objbase* module) {
        return module->preInit();
        }) != OK)
    {
        return RET_ERROR;
    }

    // post-initial all module
    if (_init_modules("postInit", [](objbase* module) {
        return module->postInit();
        }) != OK)
    {
        return RET_ERROR;
    }

    endtime = utils::GetCurrentMillisecs();
    LOG_INFO("System initialization completion time(%llums)", endtime - entrytime);

    return OK;
}
STATUS FrameWorkMgr::destory()
{
    {
        std::lock_guard<std::mutex> lock(m_objectLock);
        for (auto& iter : m_objectList)
        {
            (void)iter.second->objectPtr->destory();
        }
        m_objectList.clear();
    }
    LOG_INFO("System shutdown complete!");

    Ilog::logRelease();
    
    return OK;
}